#include "songview.h"
#include <allegro5/allegro5.h>
#include <allegro5/allegro_primitives.h>
#include "patterneditor.h"
#include "song.h"
#include "app.h"
#include "track.h"
#include "pattern.h"
#include "songitem.h"
#include "rowlabel.h"
#include "columnlabel.h"
#include "font.h"
using namespace std;

SongView::SongView(App *pApp, Rect rect, Font *pFont) 
: EditorView(pApp, rect, pFont) {

     int rowCount = m_pEditor->song()->length();
     m_pRowLabel->setCount(rowCount);

     m_vScroll.setMaximum(rowCount + 1);
     m_vScroll.setMinimum(wholeVisibleRows());
     setVScrollPos(m_vScroll.minimum());
}

void SongView::setRect(Rect rect) {
     m_rect = rect;
     if (m_pEditor->curTrackNum() * colWidth() > m_viewRect.width())
	  m_pEditor->setCurTrackNum(m_colOffset + wholeVisibleCols() - 1);
     if (m_pEditor->curSongItemNum() * m_pFont->height() > m_viewRect.height())
	  m_pEditor->setCurSongItemNum(m_rowOffset + wholeVisibleRows() - 1);
     updateRects();
}

void SongView::paint(bool all) {
     // clear
     al_draw_filled_rectangle(m_rect.x(), m_rect.y(), 
			      m_rect.x() + m_rect.width(), 
			      m_rect.y() + m_rect.height(),
			      al_map_rgb(0,0,0));
     // draw scrollbars
     m_hScroll.paint(all);
     m_vScroll.paint(all);
     // draw row labels
     m_pRowLabel->paint();
     // draw column labels
     m_pColLabel->paint();
     // draw song
     paintSong(m_viewRect);
     m_needPaint = false;
}

void SongView::paintSong(Rect rect) {
     int colwidth = colWidth();
     int viscols = visibleCols();
     int x=0, w=0;

     for (int col=0; col<viscols; col++) {
	  x = rect.x() + col * colwidth;
	  w = colwidth;
	  if (x + w > rect.x() + rect.width())
	       w = rect.x() + rect.width() - x;
	  paintTrack(col+m_colOffset, Rect(x, rect.y(), w, rect.height()));
     }
}

void SongView::paintTrack(int col, Rect rect) {

     int colwidth = colWidth();
     int visrows = visibleRows();
     int linex=0, y=0, h=0;
     Song *pSong = m_pEditor->song();
     if (!pSong)
	  return;

     Track *pTrack = NULL;
     SongItem *pItem = NULL;

     ALLEGRO_COLOR cursorColor = m_pApp->skin()->color(Skin::CursorColor);

     pTrack = pSong->trackAt(col);
     if (!pTrack)
	  return;
     // draw rows
     Rect rowRect(0,0,0,0);
     int length = pSong->length();
     for (int row=0; row<visrows; row++) {
	  if (row >= length)
	       break;
	  pItem = pTrack->songItemAt(m_rowOffset+row);
	  if (!pItem)
	       continue;
	  y = rect.y() + row * m_pFont->height();
	  if (y > rect.y() + rect.height())
	       break;
	  h = m_pFont->height();
	  if (y + h > rect.y() + rect.height())
	       h = rect.y() + rect.height() - y;
	  rowRect.setX(rect.x());
	  rowRect.setY(y);
	  rowRect.setWidth(rect.width());
	  rowRect.setHeight(h);
	  // highlight if this row is the one being played
	  if (row + m_rowOffset == m_playerRow) {
	       ALLEGRO_COLOR c;
	       if (m_playing)
		    c = m_pApp->skin()->color(Skin::PlayingColor);
	       else
		    c = m_pApp->skin()->color(Skin::StoppedColor);
	       al_draw_filled_rectangle(rowRect.x() + 1, rowRect.y(), 
					rowRect.x() + rowRect.width(), 
					rowRect.y() + rowRect.height(), c);
	  }
	  paintItem(pItem, rowRect);
     }
     // draw track separator if inside rect
     linex = rect.x() + colwidth;
     if (linex <= rect.x() + rect.width()) {
	  al_draw_line(linex, rect.y(), linex, rect.y() + rect.height(),
		       al_map_rgb(255,255,255),
		       EditorView::LineThickness);
     }
}

void SongView::paintItem(SongItem *pItem, Rect rect) {

     int w=0, h=0;
     Song *pSong = m_pEditor->song();
     if (!pSong)
	  return;

     int charx=m_pEditor->curItemField();
     int charw=1;
     const ALLEGRO_COLOR cursorColor = m_pApp->skin()->color(Skin::CursorColor);

     // highlight if cursor col/row is editor's current one
     if (pItem == m_pEditor->curSongItem()) {
	  int x1 = rect.x() + m_pFont->width() * charx + 2;
	  int y1 = rect.y();
	  int x2 = x1 + m_pFont->width()*charw;
	  int y2 = y1 + m_pFont->height();
	  al_draw_filled_rectangle(x1,y1,x2,y2,cursorColor);
     }
     // make sure event paints within rect
     w = m_pFont->width() * SongItem::strlen;
     if (w > rect.width())
	  w = rect.width();
     h = m_pFont->height();
     if (h > rect.height())
	  h = rect.height();
     int x = rect.x() + 2;
     w -= 2;
     // paint item
     m_pFont->print(pItem->toString(), Rect(x, rect.y(), w, h));
}

void SongView::keyDown(int keycode, int modifiers, int unichar) {
     int action = m_pApp->keyBindings()->keyAction(keycode, modifiers);
     int viscols = wholeVisibleCols();
     SongItem *pItem = m_pEditor->curSongItem();
     int num=0;
     switch (action) {
     case KeyBindings::RowDown:
	  decRow();
	  refresh();
 	  break;
     case KeyBindings::RowUp:
	  incRow();
	  m_pRowLabel->setCount(m_pEditor->song()->length());
	  m_vScroll.setMaximum(m_pEditor->song()->length());
	  refresh();
 	  break;
     case KeyBindings::FieldDown:
 	  m_pEditor->decItemField();
	  if (m_pEditor->curTrackNum() - m_colOffset < 0)
	       setColOffset(m_colOffset-1);
	  refresh();
 	  break;
     case KeyBindings::FieldUp:
 	  m_pEditor->incItemField();
	  if (m_pEditor->curTrackNum() - m_colOffset >= viscols)
	       setColOffset(m_colOffset+1);
	  refresh();
 	  break;
     default:
	  if (!pItem)
	       break;
	  if (m_pEditor->curItemField() == PatternEditor::Number0 && 
		     KeyBindings::keyIsHex(keycode)) {
	       if (pItem->pattern()) {
		    num = (pItem->pattern()->number() & 0x0f) + \
			 (KeyBindings::hexKeyInt(keycode) << 4);
	       } else {
		    num = KeyBindings::hexKeyInt(keycode) << 4;
	       }
	       m_pEditor->setCurPatternNum(num);
	       m_pEditor->incItemField();
	       refresh();
	       break;
	  } else if (m_pEditor->curItemField() == PatternEditor::Number1 && 
		     KeyBindings::keyIsHex(keycode)) {
	       if (pItem->pattern()) {
		    num = (pItem->pattern()->number() & 0xf0) +	\
			 (KeyBindings::hexKeyInt(keycode) & 0x0f);
	       } else {
		    num = KeyBindings::hexKeyInt(keycode);
	       }
	       m_pEditor->setCurPatternNum(num);
	       refresh();
	       break;
	  }
     }
}

void SongView::decRow() {
     m_pEditor->setCurSongItemNum(m_pEditor->curSongItemNum() - 1);
     if (m_pEditor->curSongItemNum() - m_rowOffset < 0)
	  setRowOffset(m_rowOffset-1);
}

void SongView::incRow() {
     if (m_pEditor->curSongItemNum() + 1 >= m_pEditor->song()->length())
	  m_pEditor->song()->setLength(m_pEditor->curSongItemNum() + 2);
     m_pEditor->setCurSongItemNum(m_pEditor->curSongItemNum() + 1);
     if (m_pEditor->curSongItemNum() - m_rowOffset >= wholeVisibleRows())
	  setRowOffset(m_rowOffset+1);
}

void SongView::update() {
     if (m_playerRow != m_pApp->player()->songItemNum()) {
	  m_playerRow = m_pApp->player()->songItemNum();
	  refresh();
     }
     if (m_pApp->player()->playing() != m_playing) {
	  m_playing = m_pApp->player()->playing();
	  refresh();
     }
     if (m_pEditor->song()->length() != m_pRowLabel->count()) {
	  m_pRowLabel->setCount(m_pEditor->song()->length());
	  refresh();
     }
}
